﻿/*
题目: LFU 缓存

请你为 最不经常使用（LFU）缓存算法设计并实现数据结构。

实现 LFUCache 类：

LFUCache(int capacity) - 用数据结构的容量 capacity 初始化对象
int get(int key) - 如果键 key 存在于缓存中，则获取键的值，否则返回 -1 。
void put(int key, int value) - 如果键 key 已存在，则变更其值；如果键不存在，请插入键值对。当缓存达到其容量 capacity 时，则应该在插入新项之前，移除最不经常使用的项。在此问题中，当存在平局（即两个或更多个键具有相同使用频率）时，应该去除 最久未使用 的键。
为了确定最不常使用的键，可以为缓存中的每个键维护一个 使用计数器 。使用计数最小的键是最久未使用的键。

当一个键首次插入到缓存中时，它的使用计数器被设置为 1 (由于 put 操作)。对缓存中的键执行 get 或 put 操作，使用计数器的值将会递增。

函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

https://leetcode.cn/problems/lfu-cache/description/
*/

#include <iostream>
#include <random>
#include <string>
#include <vector>
#include <list>
#include "TreeNode.hpp"
#include "ListNode.hpp"
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <unordered_map>
#include <unordered_set>
#include <algorithm>
#include <functional>

using namespace std;

struct Node {
    int key;
    int value;
    int count;
    Node* prev;
    Node* next;

    Node(int key, int value, int count = 1)
        : key(key)
        , value(value)
        , count(count)
        , prev(nullptr)
        , next(nullptr)
    {}
};

class List {
public:
    List()
        : head(new Node(0, 0))
        , tail(head)
    {}

    // 弹出最前面的元素
    Node* pop_front() {
        return pop(head->next);
    }

    Node* pop(Node* nd) {
        if (nd == tail) {
            tail = nd->prev;
            tail->next = nullptr;
        }
        else {
            nd->prev->next = nd->next;
            nd->next->prev = nd->prev;
        }

        return nd;
    }

    // 往最后插入元素
    void push_back(Node* nd) {
        tail->next = nd;
        nd->prev = tail;

        tail = nd;
        tail->next = nullptr;
    }

    bool empty() {
        return head->next == nullptr;
    }

private:
    Node* head;
    Node* tail;
};

class LFUCache {
public:
    LFUCache(int capacity)
        : cap(capacity)
        , min_count(0)
    {}

    int get(int key) {
        auto it = key2node.find(key);
        if (it == key2node.end())   return -1;

        // 开始调整
        Node* nd = it->second;
        List& ls = count2list[nd->count];

        ls.pop(nd);                             // 弹出
        if (ls.empty() && min_count == nd->count)
            min_count++;

        nd->count++;
        count2list[nd->count].push_back(nd);    // 往后追加
        return nd->value;
    }

    void lfu_pop() {
        List& ls = count2list[min_count];
        Node* nd = ls.pop_front();

        key2node.erase(nd->key);
        delete nd;

        size--;
        if (ls.empty())     min_count++;
    }

    void put(int key, int value) {
        auto it = key2node.find(key);
        if (it != key2node.end()) {         // just modify
            Node* nd = it->second;
            List& ls = count2list[nd->count];

            nd->value = value;
            ls.pop(nd);
            if (ls.empty() && min_count == nd->count) {
                min_count++;
            }

            nd->count++;
            count2list[nd->count].push_back(nd);
            return;
        }

        if (size == cap)
            lfu_pop();

        // 新增
        Node* nd = new Node(key, value);
        key2node.emplace(key, nd);
        count2list[1].push_back(nd);
        min_count = 1;
        size++;
        return;
    }

private:
    int cap;
    int size = 0;
    int min_count;
    unordered_map<int, Node*> key2node;
    unordered_map<int, List>  count2list;
};
